//=====================================================================
//
//  File:        Buffer.cs
//  Summary:    This file contains the implementation of several classes that support the
//                RemoveDuplicates transformation. These class are used to 
//                cache the rows of the incoming input buffers from the upstream 
//                component.
//  Date:        6/15/2004
//
//---------------------------------------------------------------------
//  This file is part of the Microsoft SQL Server Code Samples.
//  Copyright (C) Microsoft Corporation.  All rights reserved.
//
//  This source code is intended only as a supplement to Microsoft
//  Development Tools and/or on-line documentation.  See these other
//  materials for detailed information regarding Microsoft code samples.
//
//  THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF 
//  ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO 
//  THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
//  PARTICULAR PURPOSE.
//===================================================================== 

using System;
using System.Diagnostics;
using System.Collections;
using Microsoft.SqlServer.Dts.Pipeline;
using Microsoft.SqlServer.Dts.Pipeline.Wrapper;
using Microsoft.SqlServer.Dts.Runtime.Wrapper;
using System.Globalization;

namespace Microsoft.Samples.SqlServer.Dts
{
    #region Buffer class
    /// <summary>
    /// This class is used to cache the rows received by the RemoveDuplicates transformation during ProcessInput.
    /// </summary>
    public class Buffer
    {
        ArrayList rows;
        ColumnInfos columnInfos;

        public Buffer()
        {
            rows = new ArrayList();
            columnInfos = new ColumnInfos();
        }
        public Buffer(ColumnInfos columnInfos)
        {
            rows = new ArrayList();
            this.columnInfos = columnInfos;
        }
        /// <summary>
        /// Sorts the rows in the buffer.
        /// </summary>
        public void Sort()
        {
            rows.Sort();
        }

        #region Properties
        public int RowCount
        {
            get
            {
                return rows.Count;
            }
        }

        public int ColumnCount
        {
            get
            {
                return this.ColumnInfos.Count;
            }
        }

        public Row this[int index]
        {
            get
            {
                return (Row)rows[index];
            }
        }

        public ColumnInfos ColumnInfos
        {
            get
            {
                return columnInfos;
            }
            set
            {
                columnInfos = value;
            }
        }

        #endregion

        #region Add Row
        /// <summary>
        /// Copy the columns from the input buffer into 
        /// the internal buffer. The InputBufferColumnIndex property contains the index of the 
        /// column in the input buffer.
        /// </summary>
        /// <param name="buffer">The PipelineBuffer provided to the component in ProcessInput.</param>
        public void AddRow(PipelineBuffer buffer)
        {
            if (buffer == null)
            {
                throw new ArgumentNullException("buffer");
            }

            object[] cols = new object[this.columnInfos.Count];

            for (int col = 0; col < columnInfos.Count; col++)
            {
                ColumnInfo ci = columnInfos[col];
                cols[col] = buffer[ci.InputBufferColumnIndex];
            }
            rows.Add(new Row(cols));
        }

        public void AddRow(Row row)
        {
            rows.Add(row);
        }
        #endregion
    }
    #endregion

    #region Row Class
    /// <summary>
    /// Simple Array class containg rows and columns. The class implements the IComparable interface
    /// so the buffer class can sort the array of row objects that it contains.
    /// </summary>
    public class Row : IComparable
    {
        /// <summary>
        ///    Contains the values of each of the columns in the row.
        /// </summary>
        private object[] columnData;

        public Row(object[] columnValues)
        {
            columnData = new object[columnValues.Length];

            ///     assume the data provided is 
            /// in the same order as the column definitions.
            for (int x = 0; x < columnValues.Length; x++)
                columnData[x] = columnValues[x];
        }

        /// <summary>
        /// Provides access to the individual columns in the row.
        /// </summary>
        /// <param name="index">The location of the column to retreive.</param>
        /// <returns>The value stored in the column.</returns>
        public object this[int index]
        {
            get
            {
                return columnData[index];
            }
            set
            {
                columnData[index] = value;
            }
        }

        public override bool Equals(object obj)
        {
            Row r = obj as Row;
            return this.Equals(r);
        }

        public override int GetHashCode()
        {
            return base.GetHashCode();
        }

        public static bool operator ==(Row rowA, Row rowB)
        {
            if (rowA == null)
                throw new ArgumentNullException("rowA");

            return rowA.Equals(rowB);
        }

        public static bool operator !=(Row rowA, Row rowB)
        {
            if (rowA == null)
            {
                throw new ArgumentNullException("rowA");
            }

            return !rowA.Equals(rowB);
        }

        public static bool operator <(Row rowA, Row rowB)
        {
            throw new NotImplementedException(Properties.Resources.OperatorLT);
        }

        public static bool operator >(Row rowA, Row rowB)
        {
            throw new NotImplementedException(Properties.Resources.OperatorGT);
        }

        /// <summary>
        /// Checks to see if the provided row is an exact duplicate of this row.
        /// </summary>
        /// <param name="row">The row to compare.</param>
        /// <returns>true if each column in the row is exactly the same; otherwise false</returns>
        public bool Equals(Row row)
        {
            for (int x = 0; x < this.columnData.Length; x++)
            {
                if (columnData[x] == null && row.columnData[x] != null)
                    return false;
                else if (columnData[x] != null && row.columnData[x] == null)
                    return false;
                else if (columnData[x] != null & row.columnData[x] != null)
                {
                    if (CompareColumns(columnData[x], row.columnData[x]) != 0)
                        return false;
                }
            }
            return true;
        }
        #region IComparable Members
        /// <summary>
        /// Compare two rows.
        /// </summary>
        /// <param name="obj">A row to compare to this row.</param>
        /// <returns>
        /// Less than zero;  This instance is less than obj.  
        /// Zero; This instance is equal to obj.  
        /// Greater than zero;  This instance is greater than obj.  
        /// </returns>
        public int CompareTo(object obj)
        {
            Row compareRow = obj as Row;

            /// Compare the columns of each row.
            for (int col = 0; col < columnData.Length; col++)
            {
                if (columnData[col] != null && compareRow.columnData[col] == null)
                    return 1;
                else if (columnData[col] == null && compareRow.columnData[col] != null)
                    return -1;
                else
                {
                    if (columnData[col] != null && compareRow.columnData[col] != null)
                    {
                        int result = CompareColumns(columnData[col], compareRow[col]);

                        if (result != 0)
                            return result;
                    }
                }
            }
            return 0;
        }
        /// <summary>
        /// Compares two objects for equality.
        /// </summary>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2201:DoNotRaiseReservedExceptionTypes")]
        private static int CompareColumns(object colA, object colB)
        {
            Type aType = colA.GetType();
            Type bType = colB.GetType();

            if (!aType.Equals(bType))
                throw new Exception(Properties.Resources.TypeMismatchColumns);

            if (aType.Equals(typeof(string)))
                return ((string)colA).CompareTo((string)colB);
            else if (aType.Equals(typeof(DateTime)))
                return Convert.ToDateTime(colA, CultureInfo.CurrentUICulture).CompareTo(Convert.ToDateTime(colB, CultureInfo.CurrentUICulture));
            else if (aType.Equals(typeof(decimal)))
                return ((decimal)colA).CompareTo((decimal)colB);
            else if (aType.Equals(typeof(Guid)))
                return ((Guid)colA).CompareTo((Guid)colB);
            else if (aType.Equals(typeof(Int16)))
                return ((Int16)colA).CompareTo((Int16)colB);
            else if (aType.Equals(typeof(Int32)))
                return ((Int32)colA).CompareTo((Int32)colB);
            else if (aType.Equals(typeof(Int64)))
                return ((Int64)colA).CompareTo((Int64)colB);
            else if (aType.Equals(typeof(bool)))
                return ((bool)colA).CompareTo((bool)colB);
            else if (aType.Equals(typeof(float)))
                return ((float)colA).CompareTo((float)colB);
            else if (aType.Equals(typeof(double)))
                return ((double)colA).CompareTo((double)colB);
            else if (aType.Equals(typeof(byte)))
                return ((byte)colA).CompareTo((byte)colB);
            else if (aType.Equals(typeof(UInt16)))
                return ((UInt16)colA).CompareTo((UInt16)colB);
            else if (aType.Equals(typeof(UInt32)))
                return ((UInt32)colA).CompareTo((UInt32)colB);
            else if (aType.Equals(typeof(UInt64)))
                return ((UInt64)colA).CompareTo((UInt64)colB);
            else
                throw new Exception(Properties.Resources.UnsupportedColumnDataType);
        }
        #endregion
    }

    #endregion

    #region ColumnInfos class
    /// <summary>
    /// A simple collection class, that provides access the underlying array of ColumnInfo objects.
    /// </summary>
    public class ColumnInfos
    {
        ArrayList columnInfos;
        public ColumnInfos()
        {
            columnInfos = new ArrayList();
        }
        public ColumnInfo this[int index]
        {
            get
            {
                return (ColumnInfo)columnInfos[index];
            }
            set
            {
                columnInfos[index] = value;
            }
        }
        public int Add(ColumnInfo columnInfo)
        {
            return columnInfos.Add(columnInfo);
        }
        public int Count
        {
            get
            {
                return columnInfos.Count;
            }
        }
        #region AddColumnInformation
        /// <summary>
        /// Stores the column information of the provided input and output objects in a ColumnInfo object. 
        /// This method is called by the RemoveDuplicates component during PreExecute when the BufferManager 
        /// is available, and assumes the exact same number of input and output columns.
        /// </summary>
        /// <param name="bufferManager">The IDTSBufferManager100; used to locate the columns 
        /// in the input and output buffers.</param>
        /// <param name="output">The output of the component.</param>
        /// <param name="input">The input of the component.</param>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2201:DoNotRaiseReservedExceptionTypes"), CLSCompliant(false)]
        public void AddColumnInformation(IDTSBufferManager100 bufferManager, IDTSOutput100 output, IDTSInput100 input)
        {
            if (bufferManager == null)
            {
                throw new ArgumentNullException("bufferManager");
            }

            if (input == null)
            {
                throw new ArgumentNullException("input");
            }

            if (output == null)
            {
                throw new ArgumentNullException("output");
            }

            if (input.InputColumnCollection.Count != output.OutputColumnCollection.Count)
            {
                throw new Exception(Properties.Resources.InputColumnCollectionNotMatchOutput);
            }

            for (int x = 0; x < output.OutputColumnCollection.Count; x++)
            {
                ColumnInfo colInfo = new ColumnInfo();
                IDTSOutputColumn100 outCol = output.OutputColumnCollection[x];
                IDTSInputColumn100 inCol = input.InputColumnCollection[x];

                //    Set the buffer column index for the input column and the output column.
                colInfo.InputBufferColumnIndex = bufferManager.FindColumnByLineageID(input.Buffer, inCol.LineageID);
                colInfo.OutputBufferColumnIndex = bufferManager.FindColumnByLineageID(output.Buffer, outCol.LineageID);

                //    Save the column
                columnInfos.Add(colInfo);
            }
        }
        #endregion
    }
    #endregion

    #region ColumnInfo structure
    /// <summary>
    /// Represents a column in an output or input object.
    /// </summary>
    public struct ColumnInfo
    {
        private int inputBufferColumnIndex;
        private int outputBufferColumnIndex;

        public override bool Equals(object obj)
        {
            return base.Equals(obj);
        }

        public override int GetHashCode()
        {
            return base.GetHashCode();
        }

        public static bool operator ==(ColumnInfo ciA, ColumnInfo ciB)
        {
            return ciA.Equals(ciB);
        }

        public static bool operator !=(ColumnInfo ciA, ColumnInfo ciB)
        {
            return !ciA.Equals(ciB);
        }

        public int InputBufferColumnIndex
        {
            get
            {
                return inputBufferColumnIndex;
            }
            set
            {
                inputBufferColumnIndex = value;
            }
        }

        public int OutputBufferColumnIndex
        {
            get
            {
                return outputBufferColumnIndex;
            }
            set
            {
                outputBufferColumnIndex = value;
            }
        }
    }
    #endregion
}
